﻿using DivaScript;
using MikuMikuModel.Logs;
using PPDFramework;
using PPDFramework.PPDStructure;
using PPDFramework.PPDStructure.EVDData;
using PPDFramework.PPDStructure.PPDData;
using PPDPack;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

namespace ft_module_parser
{
    class PPD2DSC
    {
        Dictionary<string, string> dataDict = new Dictionary<string, string>();
        List<string> lyrics = new List<string>();
        List<ValueTuple<TimeSpan, float>> bpmChanges = new List<ValueTuple<TimeSpan, float>>();
        List<ValueTuple<TimeSpan, float>> slideScales = new List<ValueTuple<TimeSpan, float>>();
        const int PPD_TIME_FACTOR = 1000;
        const int PPD_X_RANGE = 800;
        const int PPD_Y_RANGE = 450;
        const int DIVA_TIME_FACTOR = 100;
        const int DIVA_X_RANGE = 480000;
        const int DIVA_Y_RANGE = 270000;
        const int DIVA_ROT_FACTOR = 1000;
        const int DIVA_POS_FACTOR = 250;
        const int DIST_PER_FRAGMENT = 9000;
        const int CHAIN_FRAGMENT_OFFSET = 8000;
        const string RIGHT_ROTATION = "#RightRotation";
        const string MARK_SPEED = "MarkSpeed";
        const string CURVATURE = "Curvature";
        const string SAME_TIMING_MARK_SPEED = "SameTimingMarkSpeed";
        public void Convert(string[] args, string outputpath = "")
        {
            string str1 = args.Length != 0 ? args[0] : string.Empty;
            if (!File.Exists(str1))
            {
                OpenFileDialog openFileDialog1 = new OpenFileDialog();
                openFileDialog1.Filter = "Shitty PPD File (*.ppd)|*.ppd|All files (*.*)|*.*";
                openFileDialog1.Title = "Select one of those shitty PPD files";
                OpenFileDialog openFileDialog2 = openFileDialog1;
                if (openFileDialog2.ShowDialog() == DialogResult.OK)
                    str1 = openFileDialog2.FileName;
            }
            if (!File.Exists(str1))
                return;
            Logs.WriteLine("Processing... " + str1);
            string directoryName = Path.GetDirectoryName(str1);
            string path1 = Path.Combine(directoryName, "data.ini");
            if (!File.Exists(path1))
                throw new Exception("Invalid data.ini path: " + path1);
            foreach (string readAllLine in File.ReadAllLines(path1))
            {
                char[] separator = new char[1] { '=' };
                string[] strArray = readAllLine.Split(separator, 2);
                if (strArray.Length > 1)
                    dataDict.Add(strArray[0], strArray[1]);
            }
            PackReader packReader = new PackReader(str1);
            MarkDataBase[] markDataBaseArray = PPDReader.Read((Stream)packReader.Read("ppd"));
            foreach (IEVDData ievdData in EVDReader.Read((Stream)packReader.Read("evd")))
            {
                TimeSpan timeSpan = TimeSpan.FromSeconds((double)ievdData.Time);
                if (ievdData is ChangeBPMEvent changeBpmEvent)
                    bpmChanges.Add(new ValueTuple<TimeSpan, float>(timeSpan, changeBpmEvent.BPM));
                else if (ievdData is ChangeSlideScaleEvent changeSlideScaleEvent)
                    slideScales.Add(new ValueTuple<TimeSpan, float>(timeSpan, changeSlideScaleEvent.SlideScale));
            }
            string s1;
            if (!dataDict.TryGetValue("bpm", out s1))
                throw new Exception("Unable to read bpm value from data.ini");
            float result1;
            if (float.TryParse(s1, out result1))
            {
                ValueTuple<TimeSpan, float> valueTuple = new ValueTuple<TimeSpan, float>(TimeSpan.Zero, result1);
                if (bpmChanges.Count > 0)
                    bpmChanges[0] = valueTuple;
                else
                    bpmChanges.Add(valueTuple);
            }
            float num1 = bpmChanges.FirstOrDefault<ValueTuple<TimeSpan, float>>().Item2;
            List<PvCommand> commands1 = new List<PvCommand>();
            int timeStamp1 = 0;
            string s2;
            float result2;
            TimeSpan timeSpan1;
            if (dataDict.TryGetValue("latency", out s2) && float.TryParse(s2, out result2))
            {
                timeSpan1 = TimeSpan.FromSeconds((double)result2);
                timeStamp1 = (int)(timeSpan1.TotalMilliseconds * 100.0);
            }
            commands1.Add(new PvCommand(timeStamp1, "MUSIC_PLAY", Array.Empty<int>()));
            commands1.Add(new PvCommand(timeStamp1, "MOVIE_PLAY", new int[1]
            {
        1
            }));
            commands1.Add(new PvCommand(timeStamp1, "MOVIE_DISP", new int[1]
            {
        1
            }));
            int num2 = 0;
            int num3 = 0;
            int num4 = 0;
            foreach (MarkDataBase markDataBase in markDataBaseArray)
            {
                MarkDataBase tgt = markDataBase;
                bool hold = false;
                TimeSpan time = TimeSpan.FromSeconds((double)tgt.Time);
                float bpmAt;
                try
                {
                    bpmAt = GetBpmAt(time);
                }
                catch (Exception ex)
                {
                    bpmAt = bpmChanges.FirstOrDefault<ValueTuple<TimeSpan, float>>().Item2;
                }
                if (tgt is ExMarkData exMarkData)
                {
                    hold = true;
                    if (tgt.ButtonType == ButtonType.L || tgt.ButtonType == ButtonType.R)
                    {
                        TimeSpan timeSpan2 = TimeSpan.FromSeconds((double)exMarkData.EndTime - (double)exMarkData.Time);
                        float slideScaleAt = GetSlideScaleAt(time);
                        TimeSpan timeSpan3 = TimeSpan.FromMilliseconds(7500.0 / (double)bpmAt / (double)slideScaleAt);
                        num3 = (int)(timeSpan3.TotalMilliseconds * 100.0);
                        num2 = (int)(timeSpan2.Ticks / timeSpan3.Ticks);
                        if ((double)((float)timeSpan2.TotalMilliseconds - (float)(num2 * (num3 / 100))) > 1.0)
                            ++num2;
                    }
                }
                int num5 = GetTargetType(tgt.ButtonType, hold);
                int num6 = (int)ConvertRange(tgt.X, 0.0f, 800f, 0.0f, 480000f);
                int num7 = (int)ConvertRange(tgt.Y, 0.0f, 450f, 0.0f, 270000f);
                int num8 = (int)(((double)RadianToDegree(-tgt.Angle) + 90.0) * 1000.0);
                int result3 = 500;
                List<MarkDataBase> list = markDataBaseArray.Where(t => (double)t.Time == (double)tgt.Time).ToList();
                bool flag1 = list.Count > 1;
                if (flag1 && list.Count == 2)
                {
                    if (num5 == 0 && list.First<MarkDataBase>((t => t != tgt)).ButtonType == ButtonType.L)
                        num5 = 12;
                    if (num5 == 1 && list.First<MarkDataBase>((t => t != tgt)).ButtonType == ButtonType.R)
                        num5 = 13;
                }
                KeyValuePair<string, string>[] parameters = tgt.Parameters;
                bool flag2 = false;
                float result4 = 1f;
                float result5 = 1f;
                float result6 = 0.0f;
                foreach (KeyValuePair<string, string> keyValuePair in parameters)
                {
                    string key = keyValuePair.Key;
                    if (!(key == "#RightRotation"))
                    {
                        if (!(key == "MarkSpeed"))
                        {
                            if (!(key == "Curvature"))
                            {
                                if (key == "SameTimingMarkSpeed")
                                    float.TryParse(keyValuePair.Value, out result5);
                            }
                            else
                                float.TryParse(keyValuePair.Value, out result6);
                        }
                        else
                            float.TryParse(keyValuePair.Value, out result4);
                    }
                    else
                        flag2 = keyValuePair.Value == "1";
                }
                int result7 = (flag1 ? (int)(880.0 * (double)result5) : (int)(1200.0 * (double)result4)) * 250;
                int num9 = flag1 ? 0 : (flag2 ? 2 : -2);
                int num10 = (int)(240000.0 / (double)bpmAt);
                int timeStamp2 = (int)((double)tgt.Time * 1000.0 * 100.0) - num10 * 100;
                foreach (KeyValuePair<string, string> keyValuePair in parameters)
                {
                    string key = keyValuePair.Key;
                    if (!(key == "Amplitude"))
                    {
                        if (key == "Distance")
                            int.TryParse(keyValuePair.Value, out result7);
                    }
                    else
                        int.TryParse(keyValuePair.Value, out result3);
                }

                foreach (KeyValuePair<string, string> keyValuePair in parameters)
                {
                    if (keyValuePair.Key == "Frequency")
                    {
                        int.TryParse(keyValuePair.Value, out num9);
                        if (num9 % 2 == 0)
                            num9 *= -1;
                        num9 = flag2 ? num9 * -1 : num9;
                    }
                }

                commands1.Add(new PvCommand(timeStamp2, "TARGET_FLYING_TIME", new int[1]
                {
          num10
                }));
                commands1.Add(new PvCommand(timeStamp2, "TARGET", new int[7]
                {
          num5,
          num6,
          num7,
          num8,
          result7,
          result3,
          num9
                }));
                bool flag3 = tgt.ButtonType == ButtonType.L || tgt.ButtonType == ButtonType.R;
                if (num2 > 0 & hold & flag3)
                {
                    int num11 = tgt.ButtonType == ButtonType.L ? -1 : 1;
                    int num12 = num6 + 8000 * num11;
                    for (int index = 0; index < num2; ++index)
                    {
                        List<PvCommand> pvCommandList = commands1;
                        int timeStamp3 = timeStamp2 += num3;
                        string opcode = "TARGET";
                        int[] numArray = new int[7];
                        numArray[0] = num5;
                        num12 = numArray[1] = num12 + 9000 * num11;
                        numArray[2] = num7;
                        numArray[3] = num8;
                        numArray[4] = result7;
                        numArray[5] = result3;
                        numArray[6] = num9;
                        PvCommand pvCommand = new PvCommand(timeStamp3, opcode, numArray);
                        pvCommandList.Add(pvCommand);
                    }
                }
                ++num4;
            }
            string path2 = Path.Combine(directoryName, "kasi.txt");
            if (File.Exists(path2))
            {
                string[] strArray1 = File.ReadAllLines(path2);
                int num5 = 0;
                foreach (string str2 in strArray1)
                {
                    char[] separator = new char[1] { ':' };
                    string[] strArray2 = str2.Split(separator, 2);
                    if (strArray2.Length > 1)
                    {
                        string s3 = strArray2[0];
                        int timeStamp2 = 0;
                        float num6 = 0.0f;
                        ref float local = ref num6;
                        if (float.TryParse(s3, out local))
                        {
                            timeSpan1 = TimeSpan.FromSeconds((double)num6);
                            timeStamp2 = (int)(timeSpan1.TotalMilliseconds * 100.0);
                        }
                        string str3 = strArray2[1];
                        lyrics.Add(str3);
                        commands1.Add(new PvCommand(timeStamp2, "LYRIC", new int[2]
                        {
              num5++,
              -1
                        }));
                    }
                }
            }
            string s4;
            if (!dataDict.TryGetValue("end", out s4))
                throw new Exception("Unable to read song end property");
            float result8;
            if (!float.TryParse(s4, out result8))
                throw new Exception("Unable to parse song end property string: " + s4);
            timeSpan1 = TimeSpan.FromSeconds((double)result8);
            int timeStamp4 = (int)(timeSpan1.TotalMilliseconds * 100.0);
            commands1.Add(new PvCommand(timeStamp4, "END", Array.Empty<int>()));
            List<PvCommand> commands2 = PvScript.OrderListByTime(PvScript.AddTimeCommands(commands1));
            Logs.WriteLine(PvScript.CommandsToString(commands2), 1);
            string str4 = Path.Combine(directoryName, Path.GetFileNameWithoutExtension(str1) + ".dsc");
            PvScript.WriteBinaryScript(str4, commands2, DivaScript.Format.FT, false);
            //Process.Start(Path.GetDirectoryName(str4));
        }

        float GetBpmAt(TimeSpan time)
        {
            if (this.bpmChanges.Count < 1)
                return 0.0f;
            if (this.bpmChanges.Count == 1)
                return this.bpmChanges[0].Item2;
            float num = this.bpmChanges[0].Item2;
            for (int index = 0; index < this.bpmChanges.Count && this.bpmChanges[index].Item1 <= time; ++index)
                num = this.bpmChanges[index].Item2;
            return num;
        }

        float GetSlideScaleAt(TimeSpan time)
        {
            if (this.slideScales.Count < 1)
                return 1f;
            if (this.slideScales.Count == 1)
                return this.slideScales[0].Item2;
            float num = this.slideScales[0].Item2;
            for (int index = 0; index < this.slideScales.Count && this.slideScales[index].Item1 <= time; ++index)
                num = this.slideScales[index].Item2;
            return num;
        }

        private static int GetTargetType(ButtonType buttonType, bool hold)
        {
            switch (buttonType)
            {
                case ButtonType.Square:
                case ButtonType.Left:
                    return !hold ? 3 : 7;
                case ButtonType.Cross:
                case ButtonType.Down:
                    return !hold ? 2 : 6;
                case ButtonType.Circle:
                case ButtonType.Right:
                    return !hold ? 1 : 5;
                case ButtonType.Triangle:
                case ButtonType.Up:
                    return !hold ? 0 : 4;
                case ButtonType.R:
                    return !hold ? 13 : 16;
                case ButtonType.L:
                    return !hold ? 12 : 15;
                default:
                    return 0;
            }
        }

        private static float RadianToDegree(float angle)
        {
            return angle * 57.29578f;
        }

        private static float ConvertRange(float s, float a1, float a2, float b1, float b2)
        {
            return b1 + (float)(((double)s - (double)a1) * ((double)b2 - (double)b1) / ((double)a2 - (double)a1));
        }
    }
}
